// 利用阻塞队列实现生产消费者模型

#ifndef __BLOCK_QUEUE_HPP__
#define __BLOCK_QUEUE_HPP__

#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <queue>

template<typename T>

class BlockQueue
{
private:
    bool isFull()
    {
        return qe.size() == max_size;
    }
    bool isEmpty()
    {
        return qe.empty();
    }

public:
BlockQueue(int cap):max_size(cap)
{
    pthread_mutex_init(&mutext,NULL);
    pthread_cond_init(&consumer,NULL);
    pthread_cond_init(&procter,NULL);
}

void Enque(T &in)
{
    pthread_mutex_lock(&mutext);

    //如果队列已满
    //这里我么必需要用while循环判断
    //当判断为满时，线程释放锁，让其它所有线程去相互竞争，竞争到锁的线程可能还是生产者
    while(isFull())//防止伪唤醒
    {
        procter_wait++;
        //这里需要传入第二个参数
        //当前进行等待时，当前线程还持有锁，如果线程抱着锁去等待，线程将会永远处于阻塞状态
        //传入相应的锁参数后，函数在进入后将会把持有的锁释放
        pthread_cond_wait(&procter,&mutext);
        //满足相应的条件时，将会唤醒线程，并对线程重新申请锁

        procter_wait--;
    }

    qe.push(in);

    if(consumer_wait>0)
    {
        pthread_cond_signal(&consumer);
    }
    pthread_mutex_unlock(&mutext);
    
}

void Pop(T &out)
{
    pthread_mutex_lock(&mutext);
    while(isEmpty())
    {
        consumer_wait++;
        pthread_cond_wait(&consumer,&mutext);
        consumer_wait--;
    }
    out =qe.front();
    qe.pop();
    if(procter_wait>0)
    {
        pthread_cond_signal(&procter);
    }
    pthread_mutex_unlock(&mutext);
    
}

private:
    std::queue<T> qe;
    pthread_mutex_t mutext;
    pthread_cond_t consumer;
    pthread_cond_t procter;

    int max_size;
    int consumer_wait;
    int procter_wait;
};

#endif